﻿// Decompiled with JetBrains decompiler
// Type: Netick.Unity.Network
// Assembly: Netick.Unity, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: E128C7FD-39ED-4452-86AC-6EC748457030
// Assembly location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.Unity.dll
// XML documentation location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.Unity.xml

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.SceneManagement;

#nullable disable
namespace Netick.Unity;

/// <summary>
/// Manages Netick and its sandboxes, and is used to start and shut down Netick.
/// </summary>
[HideInInspector]
[DisallowMultipleComponent]
public sealed class Network : MonoBehaviour
{
  /// <summary>The version of Netick.</summary>
  public const string Version = "0.14.73";
  private static Network _instance;
  private bool _started;
  private NetickConfig _config;
  private ReflectionData _reflectionData;
  private ShutdownRequest _shutdownRequested;
  internal List<NetworkSandbox> _sandboxes = new List<NetworkSandbox>(4);
  internal List<NetworkSandbox> _sandboxesCache = new List<NetworkSandbox>(4);
  private string[] _sceneNames;

  /// <summary>The start mode of Netick.</summary>
  public static StartMode StartMode => Network._instance._startMode;

  /// <summary>Returns true if Netick is running, false otherwise.</summary>
  public static bool IsRunning
  {
    get => (UnityEngine.Object) Network._instance != (UnityEngine.Object) null && Network._instance._started;
  }

  /// <summary>
  /// Returns the list of all sandboxes currently running in Netick.
  /// </summary>
  public static IReadOnlyList<NetworkSandbox> Sandboxes
  {
    get => (IReadOnlyList<NetworkSandbox>) Network._instance._sandboxes;
  }

  /// <summary>
  /// Returns the singleton instance of the Network class, which manages Netick.
  /// </summary>
  public static Network Instance => Network._instance;

  /// <summary>
  /// Returns the configuration (of the first sandbox) used by Netick.
  /// </summary>
  public static NetickConfig Config => Network._instance._config;

  /// <summary>
  /// Returns true if Netick is running in headless mode (batch mode), false otherwise.
  /// </summary>
  public static bool IsHeadless => Application.isBatchMode;

  internal StartMode _startMode { get; set; }

  internal Network()
  {
  }

  private void Awake()
  {
    if ((UnityEngine.Object) Network._instance != (UnityEngine.Object) null && (UnityEngine.Object) Network._instance != (UnityEngine.Object) this)
      UnityEngine.Object.Destroy((UnityEngine.Object) this.gameObject);
    else
      Network._instance = this;
  }

  internal void InternalDestroy() => UnityEngine.Object.Destroy((UnityEngine.Object) this.gameObject);

  private void OnDestroy()
  {
    if (!((UnityEngine.Object) Network._instance == (UnityEngine.Object) this))
      return;
    Network._instance = (Network) null;
  }

  /// <summary>
  /// Initializes Netick. This is automatically called when you start Netick.
  /// <para>If you don't provide a <see cref="T:Netick.Unity.NetickConfig" />, Netick will use the default config, which can be modified/found in (Netick -&gt; Settings).</para>
  /// </summary>
  public static void Init(NetickConfig config = null)
  {
    if ((UnityEngine.Object) Network.Instance != (UnityEngine.Object) null)
      return;
    Application.runInBackground = true;
    if (Application.platform == RuntimePlatform.OSXServer || Application.platform == RuntimePlatform.OSXPlayer || Application.platform == RuntimePlatform.IPhonePlayer)
      Debug.LogError((object) "Netick: for Netick to work properly in iOS and Mac, you must manually enable Run In Background in Edit → Project Settings → Player.");
    GameObject target = new GameObject("Netick Manager");
    target.hideFlags = HideFlags.HideInHierarchy;
    target.AddComponent<Network>();
    UnityEngine.Object.DontDestroyOnLoad((UnityEngine.Object) target);
    Network.Instance.InitSceneNames();
    Network.Instance._config = !((UnityEngine.Object) config != (UnityEngine.Object) null) ? Resources.Load<NetickConfig>("netickConfig") : config;
    if (Network.Config.PhysicsType == PhysicsType.Physics3D)
      Physics.autoSimulation = false;
    if (Network.Config.PhysicsType == PhysicsType.Physics2D)
      Physics2D.simulationMode = SimulationMode2D.Script;
    NetickLogger.SetLogger((INetickLogger) new UnityLogger());
    Network.Instance._reflectionData = new ReflectionData();
  }

  public static NetickConfig CloneDefaultConfig()
  {
    return UnityEngine.Object.Instantiate<NetickConfig>(Resources.Load<NetickConfig>("netickConfig"));
  }

  /// <summary>
  /// Shuts down Netick and destroys all sandboxes. The shutdown will occur in the next frame. For immediate shutdown, use: <see cref="M:Netick.Unity.Network.ShutdownImmediately(System.Boolean)" />
  /// </summary>
  public static void Shutdown(bool destroyAllNetworkObjects = false)
  {
    if (!((UnityEngine.Object) Network.Instance != (UnityEngine.Object) null))
      return;
    Network.Instance._shutdownRequested = new ShutdownRequest()
    {
      Shutdown = true,
      DestroyAllNetworkObjects = destroyAllNetworkObjects
    };
  }

  /// <summary>
  /// Shuts down Netick and destroys all sandboxes immediately.
  /// </summary>
  public static void ShutdownImmediately(bool destroyAllNetworkObjects = false)
  {
    if (!((UnityEngine.Object) Network.Instance != (UnityEngine.Object) null))
      return;
    Network.PerformShutdown(destroyAllNetworkObjects, true);
  }

  /// <summary>Shuts down a specific sandbox.</summary>
  /// <param name="sandbox">The sandbox to shut down.</param>
  public static void ShutdownSandbox(NetworkSandbox sandbox, bool destroyAllNetworkObjects = false)
  {
    if (!Network.Instance._sandboxes.Contains(sandbox))
      return;
    sandbox.Shutdown(destroyAllNetworkObjects);
    Network.Instance._sandboxes.Remove(sandbox);
  }

  private static void PerformShutdown(bool destroyAllNOs, bool isImmediate)
  {
    if (!((UnityEngine.Object) Network.Instance != (UnityEngine.Object) null))
      return;
    Network.Instance._sandboxesCache.Clear();
    for (int index = Network.Instance._sandboxes.Count - 1; index >= 0; --index)
    {
      Network.Instance._sandboxes[index].IsVisible = true;
      Network.Instance._sandboxes[index].Shutdown(destroyAllNOs);
    }
    StaticUnitySceneManager.Reset();
    Network.Instance._sandboxes.Clear();
    Network.Instance._sandboxesCache.Clear();
    UnityEngine.Object.Destroy((UnityEngine.Object) Network.Instance.gameObject);
    Network.Instance._started = false;
    Network.Instance.InternalDestroy();
    if (!isImmediate)
      return;
    Network._instance = (Network) null;
  }

  /// <summary>Focus on a specific sandbox.</summary>
  /// <param name="sandbox">The sandbox to focus on.</param>
  public static void Focus(NetworkSandbox sandbox)
  {
    if (!Network.Instance._sandboxes.Contains(sandbox))
      return;
    for (int index = 0; index < Network.Instance._sandboxes.Count; ++index)
    {
      NetworkSandbox sandbox1 = Network.Instance._sandboxes[index];
      sandbox1.IsVisible = !((UnityEngine.Object) sandbox1 != (UnityEngine.Object) sandbox);
      sandbox1.InputEnabled = !((UnityEngine.Object) sandbox1 != (UnityEngine.Object) sandbox);
    }
  }

  /// <summary>Starts both a client (or clients) and a server.</summary>
  /// <param name="serverPort">Network port.</param>
  /// <param name="numOfClients">Number of client sandboxes to create.</param>
  /// <returns></returns>
  [Obsolete("This is obsolete. Use Launch instead.")]
  public static Network.LaunchResults StartAsServerAndClient(
    NetworkTransportProvider transportProvider,
    int serverPort,
    GameObject sandboxPrefab = null,
    int numOfClients = 1)
  {
    return Network.StartAsMultiplePeers(transportProvider, serverPort, sandboxPrefab, numOfClients: numOfClients);
  }

  /// <summary>Starts multiple peers together.</summary>
  /// <param name="serverPort">Network port.</param>
  /// <param name="numOfClients">Number of clients to create.</param>
  /// <param name="startAServer">Should we start a server too.</param>
  /// <returns></returns>
  public static Network.LaunchResults StartAsMultiplePeers(
    NetworkTransportProvider transportProvider,
    int port,
    GameObject sandboxPrefab = null,
    bool startAServer = true,
    bool startServerAsHost = true,
    int numOfClients = 1,
    NetickConfig config = null)
  {
    return Network.Launch(StartMode.MultiplePeers, new LaunchData()
    {
      Config = config,
      Port = port,
      SandboxPrefab = sandboxPrefab,
      NumberOfClients = numOfClients,
      NumberOfServers = startAServer ? 1 : 0,
      TransportProvider = transportProvider,
      RunServersAsHosts = startServerAsHost
    });
  }

  /// <summary>Starts Netick as a client.</summary>
  /// <returns>The sandbox representing the client</returns>
  public static NetworkSandbox StartAsClient(
    NetworkTransportProvider transportProvider,
    GameObject sandboxPrefab = null,
    NetickConfig config = null)
  {
    return Network.Launch(StartMode.Client, new LaunchData()
    {
      Config = config,
      SandboxPrefab = sandboxPrefab,
      TransportProvider = transportProvider
    }).Clients[0];
  }

  /// <summary>Starts Netick as a client.</summary>
  /// <returns>The sandbox representing the client</returns>
  public static NetworkSandbox StartAsClient(
    NetworkTransportProvider transportProvider,
    int port,
    GameObject sandboxPrefab = null,
    NetickConfig config = null)
  {
    return Network.Launch(StartMode.Client, new LaunchData()
    {
      Config = config,
      Port = port,
      SandboxPrefab = sandboxPrefab,
      TransportProvider = transportProvider
    }).Clients[0];
  }

  /// <summary>Starts Netick in single player mode.</summary>
  /// <returns>The sandbox representing the server</returns>
  public static NetworkSandbox StartAsSinglePlayer(GameObject sandboxPrefab = null, NetickConfig config = null)
  {
    return Network.Launch(StartMode.SinglePlayer, new LaunchData()
    {
      Config = config,
      SandboxPrefab = sandboxPrefab
    }).Servers[0];
  }

  /// <summary>Starts Netick as a host.</summary>
  /// <param name="port">Network port.</param>
  /// <returns>The sandbox representing the server</returns>
  public static NetworkSandbox StartAsHost(
    NetworkTransportProvider transportProvider,
    int port,
    GameObject sandboxPrefab = null,
    NetickConfig config = null)
  {
    return Network.Launch(StartMode.Host, new LaunchData()
    {
      Config = config,
      Port = port,
      SandboxPrefab = sandboxPrefab,
      TransportProvider = transportProvider
    }).Servers[0];
  }

  /// <summary>Starts Netick as a server.</summary>
  /// <param name="port">Network port.</param>
  /// <returns>The sandbox representing the server</returns>
  public static NetworkSandbox StartAsServer(
    NetworkTransportProvider transportProvider,
    int port,
    GameObject sandboxPrefab = null,
    NetickConfig config = null)
  {
    return Network.Launch(StartMode.Server, new LaunchData()
    {
      Config = config,
      Port = port,
      SandboxPrefab = sandboxPrefab,
      TransportProvider = transportProvider
    }).Servers[0];
  }

  public static Network.LaunchResults Launch(StartMode mode, LaunchData launchData)
  {
    if ((UnityEngine.Object) Network.Instance != (UnityEngine.Object) null && Network.Instance._started)
    {
      NetickLogger.LogError((object) "Netick: Netick is already running. Shutdown before attempting to start it again.");
      return new Network.LaunchResults();
    }
    if ((UnityEngine.Object) Network.Instance == (UnityEngine.Object) null)
      Network.Init(launchData.Config);
    if ((UnityEngine.Object) launchData.Config == (UnityEngine.Object) null)
      launchData.Config = Network.Instance._config;
    Network.Instance._startMode = mode;
    if (mode == StartMode.MultiplePeers)
    {
      NetworkSandbox[] networkSandboxArray1 = new NetworkSandbox[launchData.NumberOfClients];
      NetworkSandbox[] networkSandboxArray2 = new NetworkSandbox[launchData.NumberOfServers];
      bool flag = false;
      NetickStartMode startMode = launchData.RunServersAsHosts ? NetickStartMode.Host : NetickStartMode.Server;
      for (int index = 0; index < launchData.NumberOfServers; ++index)
      {
        bool useMainScene = !flag && index == 0;
        NetickConfig config = launchData.Configs != null ? launchData.Configs[index] : launchData.Config;
        networkSandboxArray2[index] = Network.CreateSandbox(config, startMode, launchData.SandboxPrefab, launchData.TransportProvider, launchData.Ports != null ? launchData.Ports[index] : launchData.Port, useMainScene, index.ToString());
        if (useMainScene)
          flag = true;
      }
      for (int index = 0; index < launchData.NumberOfClients; ++index)
      {
        bool useMainScene = !flag && index == 0;
        NetickConfig config = launchData.Configs != null ? launchData.Configs[index + launchData.NumberOfServers] : launchData.Config;
        networkSandboxArray1[index] = Network.CreateSandbox(config, NetickStartMode.Client, launchData.SandboxPrefab, launchData.TransportProvider, launchData.Port, useMainScene, (index + launchData.NumberOfServers).ToString());
        if (useMainScene)
          flag = true;
      }
      Network.LaunchResults launchResults = new Network.LaunchResults()
      {
        Clients = networkSandboxArray1,
        Servers = networkSandboxArray2
      };
      if (networkSandboxArray1.Length != 0)
        Network.Focus(networkSandboxArray1[0]);
      else if (networkSandboxArray2.Length != 0)
        Network.Focus(networkSandboxArray2[0]);
      Network.Instance._started = true;
      StaticUnitySceneManager.InitPhase1(launchData.NumberOfClients + launchData.NumberOfServers);
      return launchResults;
    }
    NetickStartMode startMode1 = NetickStartMode.Server;
    if (mode == StartMode.Server)
      startMode1 = NetickStartMode.Server;
    if (mode == StartMode.Host)
      startMode1 = NetickStartMode.Host;
    if (mode == StartMode.Client)
      startMode1 = NetickStartMode.Client;
    if (mode == StartMode.SinglePlayer)
      startMode1 = NetickStartMode.SinglePlayer;
    NetworkSandbox sandbox = Network.CreateSandbox(launchData.Config, startMode1, launchData.SandboxPrefab, launchData.TransportProvider, launchData.Port, name: "0");
    Network.Instance._started = true;
    StaticUnitySceneManager.InitPhase1(1);
    if (mode != StartMode.Client)
      return new Network.LaunchResults()
      {
        Servers = new NetworkSandbox[1]{ sandbox }
      };
    return new Network.LaunchResults()
    {
      Clients = new NetworkSandbox[1]{ sandbox }
    };
  }

  public static NetworkSandbox Launch(SandboxLaunchData sandbox)
  {
    if ((UnityEngine.Object) Network.Instance != (UnityEngine.Object) null && Network.Instance._started)
      return (NetworkSandbox) null;
    return Network.Launch(new List<SandboxLaunchData>()
    {
      sandbox
    })[0];
  }

  public static List<NetworkSandbox> Launch(List<SandboxLaunchData> sandboxes)
  {
    if ((UnityEngine.Object) Network.Instance != (UnityEngine.Object) null && Network.Instance._started || sandboxes == null || sandboxes.Count < 1)
      return new List<NetworkSandbox>(0);
    if ((UnityEngine.Object) Network.Instance == (UnityEngine.Object) null)
      Network.Init(sandboxes[0].Config);
    SandboxLaunchData[] array = sandboxes.OrderBy<SandboxLaunchData, bool>((Func<SandboxLaunchData, bool>) (item => !item.IsServer)).ToArray<SandboxLaunchData>();
    if (array.Length > 1)
    {
      Network.Instance._startMode = StartMode.MultiplePeers;
    }
    else
    {
      SandboxLaunchData sandboxLaunchData = array[0];
      if (sandboxLaunchData.StartMode == NetickStartMode.Server)
        Network.Instance._startMode = StartMode.Server;
      if (sandboxLaunchData.StartMode == NetickStartMode.Host)
        Network.Instance._startMode = StartMode.Host;
      if (sandboxLaunchData.StartMode == NetickStartMode.Client)
        Network.Instance._startMode = StartMode.Client;
      if (sandboxLaunchData.StartMode == NetickStartMode.SinglePlayer)
        Network.Instance._startMode = StartMode.SinglePlayer;
    }
    List<NetworkSandbox> networkSandboxList = new List<NetworkSandbox>(array.Length);
    NetworkSandbox sandbox1 = (NetworkSandbox) null;
    for (int index = 0; index < array.Length; ++index)
    {
      SandboxLaunchData sandboxLaunchData = array[index];
      if ((UnityEngine.Object) sandboxLaunchData.Config == (UnityEngine.Object) null)
        sandboxLaunchData.Config = Network.Instance._config;
      bool useMainScene = index == 0;
      NetworkSandbox sandbox2 = Network.CreateSandbox(sandboxLaunchData.Config, sandboxLaunchData.StartMode, sandboxLaunchData.SandboxPrefab, sandboxLaunchData.TransportProvider, sandboxLaunchData.Port, useMainScene, index.ToString());
      networkSandboxList.Add(sandbox2);
      if (sandbox2.IsClient && (UnityEngine.Object) sandbox1 == (UnityEngine.Object) null)
        sandbox1 = sandbox2;
    }
    if (networkSandboxList.Count == 1)
      Network.Focus(networkSandboxList[0]);
    else
      Network.Focus(sandbox1);
    Network.Instance._started = true;
    StaticUnitySceneManager.InitPhase1(array.Length);
    return networkSandboxList;
  }

  private static NetworkSandbox CreateSandbox(
    NetickConfig config,
    NetickStartMode startMode,
    GameObject prefab,
    NetworkTransportProvider transportProvider,
    int port,
    bool useMainScene = true,
    string name = "")
  {
    if ((UnityEngine.Object) transportProvider == (UnityEngine.Object) null && Network.Instance._startMode != StartMode.SinglePlayer)
      NetickLogger.LogError((object) "Netick: no transport provider found! Make sure to pass a transport provider when starting Netick.");
    NetworkSandbox sandbox;
    if ((UnityEngine.Object) prefab == (UnityEngine.Object) null)
    {
      sandbox = new GameObject().AddComponent<NetworkSandbox>();
    }
    else
    {
      GameObject gameObject = UnityEngine.Object.Instantiate<GameObject>(prefab);
      NetworkSandbox component;
      sandbox = !gameObject.TryGetComponent<NetworkSandbox>(out component) ? gameObject.AddComponent<NetworkSandbox>() : component;
    }
    sandbox.Init(config, transportProvider, startMode, !useMainScene, name, port, Network.Instance._reflectionData);
    sandbox.name = sandbox.Name + " Sandbox";
    Network.Instance._sandboxes.Add(sandbox);
    UnityEngine.Object.DontDestroyOnLoad((UnityEngine.Object) sandbox.gameObject);
    return sandbox;
  }

  private void Update() => this.InvokeUpdate();

  private void LateUpdate() => this.InvokeLateUpdate();

  private void InvokeUpdate()
  {
    this._sandboxesCache.Clear();
    for (int index = 0; index < this._sandboxes.Count; ++index)
      this._sandboxesCache.Add(this._sandboxes[index]);
    if (!StaticUnitySceneManager.IsInitialized)
      return;
    if (this._shutdownRequested.Shutdown && (UnityEngine.Object) Network.Instance != (UnityEngine.Object) null)
    {
      Network.PerformShutdown(this._shutdownRequested.DestroyAllNetworkObjects, false);
    }
    else
    {
      for (int index = 0; index < this._sandboxesCache.Count; ++index)
      {
        try
        {
          if (!this._sandboxesCache[index].IsRunning)
            this._sandboxes.Remove(this._sandboxesCache[index]);
          else
            this._sandboxesCache[index].UpdateInternal();
        }
        catch (Exception ex)
        {
          NetworkSandbox targetScript = this._sandboxesCache[index];
          NetickLogger.LogError((object) ex, (object) targetScript);
        }
      }
    }
  }

  private void InvokeLateUpdate()
  {
    StaticUnitySceneManager.Update();
    if (!StaticUnitySceneManager.IsInitialized)
      return;
    if (this._shutdownRequested.Shutdown && (UnityEngine.Object) Network.Instance != (UnityEngine.Object) null)
    {
      Network.PerformShutdown(this._shutdownRequested.DestroyAllNetworkObjects, false);
    }
    else
    {
      for (int index = 0; index < this._sandboxesCache.Count; ++index)
      {
        try
        {
          this._sandboxesCache[index].LateUpdateInternal();
        }
        catch (Exception ex)
        {
          NetworkSandbox targetScript = this._sandboxesCache[index];
          NetickLogger.LogError((object) ex, (object) targetScript);
        }
      }
      this._sandboxesCache.Clear();
    }
  }

  internal static string NameFromIndex(int BuildIndex)
  {
    string pathByBuildIndex = SceneUtility.GetScenePathByBuildIndex(BuildIndex);
    string str = pathByBuildIndex.Substring(pathByBuildIndex.LastIndexOf('/') + 1);
    return str.Substring(0, str.LastIndexOf('.'));
  }

  private void InitSceneNames()
  {
    int countInBuildSettings = SceneManager.sceneCountInBuildSettings;
    this._sceneNames = new string[countInBuildSettings];
    for (int BuildIndex = 0; BuildIndex < countInBuildSettings; ++BuildIndex)
      this._sceneNames[BuildIndex] = Network.NameFromIndex(BuildIndex);
  }

  internal int GetSceneBuildIndex(string sceneName)
  {
    if (this._sceneNames == null)
      this.InitSceneNames();
    for (int sceneBuildIndex = 0; sceneBuildIndex < this._sceneNames.Length; ++sceneBuildIndex)
    {
      if (this._sceneNames[sceneBuildIndex].Equals(sceneName))
        return sceneBuildIndex;
    }
    return -1;
  }

  public struct LaunchResults
  {
    public NetworkSandbox[] Servers;
    public NetworkSandbox[] Clients;
  }
}
